/*
实现服务注册中心接口的内存实现
创建注册中心接口和基于内存的简单实现，为后续集成 etcd 或 consul 等分布式注册中心做准备

	服务注册中心的基本接口 Registry
	基于内存的注册中心实现 MemoryRegistry
	服务注册、注销、查询和列表功能
	线程安全的并发访问控制

2025/10/06
memoryRegistry私有化，仅提供外界接口访问服务信息
2025/10/12
增加服务定位器 Locator 更新服务增加和服务注销功能配合了服务信息发布（看用哪个上下文了）
2025/10/15
增加健康检查 health，定期检查，不健康剔除
*/
package memory

import (
	"fmt"
	"gameServer/log"
	. "gameServer/registry"
	"sync"
	"time"
)

/* //* 基于内存的注册中心实现，提供服务注册、注销、查询和健康状态管理功能，支持并发安全访问和服务定位器集成 */

// // 这里是注册中心的声明，后面直接跟了一个内存注册中心 这里还是给分开了

// 内存注册中心 实现
type memoryRegistry struct {
	services map[string]*ServiceInfo // 存储服务的map：key是服务ID（保证唯一），value是服务信息指针
	mu       sync.RWMutex            // 读写锁：保证多线程并发访问map时的线程安全
	locator  Locator                 // 服务器定位接口
	// 为什么用RWMutex？读操作（如GetService）可以并发执行，写操作（如Register）独占，提高性能
}

// 设置服务定位器
func (r *memoryRegistry) SetLocator(locator Locator) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.locator = locator
	return nil
}

// 创建 内存注册中心
func NewMemoryRegistry() Registry {
	return &memoryRegistry{
		services: make(map[string]*ServiceInfo),
	}
}

// 注册服务
func (r *memoryRegistry) Register(service *ServiceInfo) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	// 设置健康状态和注册时间
	service.Healthy = true
	// 返回结构体类型，可以直接赋值，发生值拷贝，里面应该没有引用字段？总之这里还是用Now更安全
	service.Registered = time.Now()
	service.LastCheck = time.Now()

	// 将服务信息存入map：key是服务ID（唯一），value是服务信息
	// 若服务ID已存在，会覆盖旧值（因为ID唯一，通常不会重复，重复代表同一服务重启）
	r.services[service.ID] = service
	log.Infof("Service registered: %s (%s) at %s:%d", service.Name, service.ID, service.Address, service.Port)
	// 发布服务更新
	if r.locator != nil {
		go r.publishServices()
	}
	return nil
}

// 注销服务
func (r *memoryRegistry) Deregister(serviceID string) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if service, exists := r.services[serviceID]; exists {
		delete(r.services, serviceID)
		log.Infof("Service deregistered: %s (%s)", service.Name, serviceID)
		// 发布服务更新（关了某个服务，更新下去）
		if r.locator != nil {
			go r.publishServices()
		}
	}

	// // 不会出现多个，不过打印的时候不能有没有键的情况，还是遍历一下好？
	// for id, service := range r.services {
	// 	if service.ID == serviceID {
	// 		log.Infof("Service deregistered: %s", serviceID)
	// 		delete(r.services, id)
	// 		break
	// 	}
	// }
	return nil
}

// UpdateHealthStatus 更新服务健康状态
//
// 参数：
//
//	serviceID: 服务唯一ID
//	healthy: 新的健康状态（true为健康，false为不健康）
//
// 返回值：
//
//	error: 操作结果（此处固定返回nil，预留扩展）
func (r *memoryRegistry) UpdateHealthStatus(serviceID string, healthy bool) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if service, exists := r.services[serviceID]; exists {
		oldHealth := service.Healthy   // 记录旧健康状态
		service.Healthy = healthy      // 更新为新健康状态
		service.LastCheck = time.Now() // 更新最后检查时间为当前时间
		// 仅当健康状态发生变化时记录日志并发布更新
		if oldHealth != service.Healthy {
			status := "healthy"
			if !healthy {
				status = "unhealthy"
			}
			log.Warnf("Service %s health status changed to %s", serviceID, status)
			// 发布更新通知
			// 若存在定位器，异步发布服务更新
			if r.locator != nil {
				go r.publishServices()
			}
		}
	}
	return nil
}

// 发布服务列表到定位器
func (r *memoryRegistry) publishServices() {
	r.mu.Lock()
	defer r.mu.Unlock()

	services := make([]*ServiceInfo, 0, len(r.services))
	for _, service := range r.services {
		services = append(services, service)
	}
	if r.locator != nil {
		// 将服务列表同步给定位器  这里定位器应该是redis
		if err := r.locator.UpdateServices(services); err != nil {
			log.Errorf("Failed to publish services: %v", err)
		}
	}
}

// 获取服务
// 根据服务名称查询所有匹配的服务实例（一个服务可能有多个实例，如集群部署）
func (r *memoryRegistry) GetService(serviceName string) ([]*ServiceInfo, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var services []*ServiceInfo
	for _, service := range r.services {
		if service.Name == serviceName {
			services = append(services, service)
		}
	}
	return services, nil
}

// GetHealthyServices 根据服务名称查询所有健康的服务实例
//
// 参数：
//
//	serviceName: 服务名称
//
// 返回值：
//
//	[]*ServiceInfo: 健康的服务实例列表
//	error: 操作结果（此处固定返回nil，预留扩展）
func (r *memoryRegistry) GetHealthyServices(serviceName string) ([]*ServiceInfo, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var services []*ServiceInfo
	for _, service := range r.services {
		if service.Name == serviceName && service.Healthy {
			services = append(services, service)
		}
	}
	return services, nil
}

// 列出所有服务 返回注册中心中所有的服务实例，用于全量服务信息查询
func (r *memoryRegistry) ListServices() ([]*ServiceInfo, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var services []*ServiceInfo
	for _, service := range r.services {
		services = append(services, service)
	}
	return services, nil
}

// 列出所有健康服务
func (r *memoryRegistry) ListHealthyServices() ([]*ServiceInfo, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var services []*ServiceInfo
	for _, service := range r.services {
		if service.Healthy {
			services = append(services, service)
		}
	}
	return services, nil
}

func (r *memoryRegistry) GetServicesByID(serviceID string) (*ServiceInfo, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	service, exists := r.services[serviceID]
	if !exists {
		return nil, fmt.Errorf("service %s not found", serviceID)
	}
	return service, nil
}

// CleanupStaleServices 清理过期服务（长时间未检查的服务）
//
// 参数：
//
//	timeout: 超时阈值，超过此时长未检查的服务视为过期
//
// 返回值：
//
//	int: 被清理的服务数量
func (r *memoryRegistry) CleanupStaleServices(timeout time.Duration) int {
	r.mu.Lock()
	defer r.mu.Unlock()
	// 计算过期时间点：当前时间减去超时阈值（减去以后，如果没到这个时间，说明超时了，比这个时间大说明没超时）
	// 用最后检查的时间做比较
	cutoff := time.Now().Add(-timeout)
	// 当参数是 负数 时，代表 “从当前时间往前回退指定间隔”；若为正数，则是 “往后推迟”
	count := 0
	for id, service := range r.services {
		if service.LastCheck.Before(cutoff) {
			delete(r.services, id)
			count++
			log.Infof("Cleaned up stale service: %s (%s)", service.Name, id)
		}
	}
	// 若有服务被清理且存在定位器，发布服务更新
	if count > 0 && r.locator != nil {
		go r.publishServices()
	}
	return count
}
